package wheel;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import wheel.Game.Category;
import wheel.Puzzle;

/**
 * Pat (Sajak) is the driving class and directs the Wheel of Fortune game play.
 * 
 * @author 		Jack Myers, Steve Gennaoui
 * @version 	1.2
 *
 */
public class Pat {
	
	private static Scanner in = new Scanner(System.in);  // To get input from human contestants
	private static boolean demoMode = false;
	
	public static void main(String[] args) throws IOException {
		
		// Game setup: contestants and wheel
		ArrayList<Contestant> contestants = new ArrayList<Contestant>();
		
		// Create contestants
		Contestant human1 = new Person(getPersonName());
		/* Parameters to create an  AIagent
		 * name
		 * debugMode
		 * winningsThreshold				Will spin until we get that much $
		 * perWordPossibilityThreshold		Will stop searching databases once this # of words found
		 * letterCertaintyThreshold    		Will guess based on location of a letter only if greater than this.
		 */
		Contestant redQueen = new AIagent3("Red Queen3", true, 1000, 100, 50);
		Contestant hal = new EagerSolver("HAL", false, 10);  // parameter 3 is perWordPossibilityThreshold
		Contestant slave = new ReluctantSolver("Slave", false, 10); // parameter 3 is perWordPossibilityThreshold

		Puzzle roundTwoPuzzle = new Puzzle(Game.getRandomPhrase());
		setDemoMode();

		if (!demoMode) {
			//contestants.add(human1);
			contestants.add(redQueen);
			((Computer) redQueen).setDebugMode(false);
		}
		else {
			// Demo mode
			contestants.add(human1);
			contestants.add(slave);
			contestants.add(hal);
			roundTwoPuzzle = getRoundTwoPuzzle();
		}
		System.out.println("Let's play WHEEL OF FORTUNE!");

		


		displayScoreboard(contestants);
		Wheel myWheel = new Wheel(1);
		int numberOfRounds = 60;
		
		// Game variables
		Contestant currentContestant;			// Keeps track of the current contestant
		Puzzle myPuzzle;						// The current puzzle
		Game.ContestantAction contestantAction;	// The current contestant action (spin, vowel, solve)
		Game.ContestantAction freePlayAction;	// The current free play action (consonant, vowel, solve)
		Wedge currentWedge = null;				// The currently active wedge of the wheel
		Game.WedgeAction wedgeAction;			// The current action on the active wedge of the wheel
		int lettersWorth;						// $ value for successful consonant guesses based on wedge
		int lettersFound;						// The number of times the guessed letter appears in phrase
		boolean freePlayMode = false;           // Toggle that is set when Free Play wedge is active
		String suggestedSolution;				// Contestant's suggestion for the solution
	
        for (int round = 1; round <= numberOfRounds; round++) {		

        	// Round setup:  puzzle and starting contestant
        	
        	/*
        	 * To run a specific puzzle test, select a number from the test bank
        	 * 		WHAT_ARE_YOU_DOING		0-2
        	 * 		Plurals: "Likely S"		3-6
        	 * 		Plurals: "Likely AND"	3-8
        	 * 		Punctuation				9-12  (?, ', &, -)	
        	 * 		totally random			13-19 (deprives humans of a contextual advantage)
        	 * 		popular culture			20-22 ("Darth", "Jedi", "Gigli")
        	 * 		similar patterns		23,24 ("Anchorman", "Inception")
        	 * 		rarer letters			25 ("Sly as a Fox"), 18, 22
        	 * 		common letters			26-28 ("Rent", "Ellis Island", "Aristotle")
        	 * 		short puzzles			22, 26
        	 * 		long puzzles			2, 20, 14
        	 * 		old standards			29-31 ("Wings","Deli","Chowder")
        	 *      invoke_words only		32
        	 *      DEMO PHRASE             33
        	 */

			//myPuzzle = new Puzzle(Game.getPhrase(24)); 		// Use puzzle from the test bank
			myPuzzle = new Puzzle(Game.getRandomPhrase());	// Use random puzzle from text file
			//myPuzzle = new Puzzle(new Phrase("Pearl Bracelet", Game.Category.THING));
			//myPuzzle = new Puzzle(new Phrase("Raising Money for Jerry's Kids", Game.Category.WHAT_ARE_YOU_DOING));
			//myPuzzle = new Puzzle(new Phrase("Haven't Met You Yet", Game.Category.SONG_TITLE));
			//myPuzzle = new Puzzle(new Phrase("Roasted Macadamia Nuts", Game.Category.FOOD_AND_DRINK));
			//myPuzzle = new Puzzle(new Phrase("Woodpeckers", Game.Category.LIVING_THINGS));
			myPuzzle = new Puzzle(new Phrase("Unequiovacable and Mysterious Salamanders",Game.Category.TOTALLY_RANDOM));
			if (demoMode && round == 1) myPuzzle = roundTwoPuzzle;
			
			
			for (Contestant myContestant: contestants) {
				if (myContestant instanceof Computer) {
					/* Give the Computer contestant the Puzzle object, so it can load the answer,
					   which is initially all *'s and punctuation.
					 */
					Computer myComputerContestant =  (Computer) myContestant;
					myComputerContestant.setCurrentPuzzle(myPuzzle);				
				}
			}	
		
			// Announce the round
			currentContestant = contestants.get((int) (Math.random()*(contestants.size())));
			System.out.println("We have a new puzzle, its category is " + 
								myPuzzle.getPhrase().getCategory() + ".");
			myPuzzle.displayAnswer();
			Game.makeSound("new puzzle");
			
	        boolean solved = false;
	        contestantAction = Game.ContestantAction.WAVE_TO_VANNA;   // Dummy action with no game effect
	        freePlayAction = Game.ContestantAction.THINK;             // Dummy action with no game effect
        
			while (!solved) {
				// Pat gives wheel control
				System.out.printf("\nIt is ");
				//if (freePlayMode) System.out.printf("still "); 
				System.out.printf("%s's turn.\n", currentContestant.getName());
				currentContestant.speak();
				
				if (!freePlayMode) {
					contestantAction = currentContestant.getContestantAction();  // Get Contestant's choice
				}
				
				if ((contestantAction == Game.ContestantAction.SPIN_WHEEL && !myPuzzle.isMoreConsonants())
					|| (contestantAction == Game.ContestantAction.BUY_VOWEL && !myPuzzle.isMoreVowels())) {
					/* You can't spin the wheel if all consonants are guessed, nor can you buy a vowel
					 * if there aren't any left.  Assign a contestant action that will do nothing, and
					 * Pat will reask the contestant for an action after chastising him or her.        */
					contestantAction = Game.ContestantAction.DOZE_OFF;
					System.out.println("Are you dosing off?");
				}
						
				// Pat will interact with the contestants differently, based their contestant action
				switch (contestantAction) {			
				case SPIN_WHEEL:
				case FREE_CONSONANT:	
					if (!freePlayMode) {
						currentWedge = myWheel.spin();
						wedgeAction = currentWedge.getAction();
					}
					else {
						// Free CONSONANT in Free Play Mode.  Acts like MONEY wedge.
						wedgeAction = Game.WedgeAction.MONEY;
					}
					lettersWorth = currentWedge.getValue();
					
					// Pat will assume different behaviors based on the wedge action.
					switch (wedgeAction) {
					case MONEY:
						System.out.printf("The value you spun is $%d.\n", lettersWorth);
						lettersFound = myPuzzle.evaluateGuess(currentContestant.getConsonant());
						if (lettersFound > 0) {
							// The consonant was found.
							currentContestant.incrementWinnings(lettersFound * lettersWorth); 
	   						if (myPuzzle.getAnswer().indexOf('*') == -1) {	
	   							solved = true;	
	   						}
						}
						else {
							// The consonant was not found, but did contestant land on Free Play wedge?
							if (freePlayMode) {
								// No penalty for incorrect guess.
							}
							else {
								currentContestant = contestants.get(getNextContestant(contestants, currentContestant));					
							}
						}
						if (freePlayMode) {
							// Turn it off, it was already used.
							freePlayMode = false;
						}
						break;
					case FREE_PLAY:	
						Game.makeSound("free play");
						freePlayMode = true;
						System.out.printf(Game.FREE_PLAY_STRING, lettersWorth);
						freePlayAction = currentContestant.getFreePlayAction();
						// Set the contestantAction to the freePlay Action for the next loop iteration
						contestantAction = freePlayAction;
						break;
					case BANKRUPT:
						Game.makeSound("bankrupt");
						System.out.printf("Bankrupt!  Awww. %s, I am sorry.\n", currentContestant.getName());
						currentContestant.setWinnings(0);
						currentContestant = contestants.get(getNextContestant(contestants, currentContestant));					
						contestantAction = Game.ContestantAction.WAVE_TO_VANNA;
						break;
					case LOSE_TURN:
						Game.makeSound("buzzer");
						System.out.printf("Sorry, %s, you lose a turn.\n", currentContestant.getName());
						currentContestant = contestants.get(getNextContestant(contestants, currentContestant));					
						break;
					default:
						System.out.println(currentWedge.getAction());
						break;	
					}
					break;
				case BUY_VOWEL:
	                if (currentContestant.getWinnings() >= Game.VOWEL_COST) {
						// Dock if buying, but not if free vowel
	                	// Decrement winnings by cost of a vowel
	                	currentContestant.incrementWinnings(-Game.VOWEL_COST);
	                	// Check to see if the vowel is in the puzzle
	    				lettersFound = myPuzzle.evaluateGuess(currentContestant.getVowel());
						if (lettersFound < 1 ) {
							currentContestant = contestants.get(getNextContestant(contestants, currentContestant));
	    				}
	   					else
	   						// The vowel was found.  Check to see if puzzle solved.
	   						if (myPuzzle.getAnswer().indexOf('*') == -1) {
	   							solved = true;	
	   						}
	                }
	                else System.out.println("You don't have enough money to buy a vowel.");
					break;
				case FREE_VOWEL:
	                // Check to see if the vowel is in the puzzle
	    			lettersFound = myPuzzle.evaluateGuess(currentContestant.getVowel());
					if (lettersFound < 1 ) {
						// The vowel was found.  Check to see if puzzle solved.
	   					if (myPuzzle.getAnswer().indexOf('*') == -1) {
	   						solved = true;	
	   					}
	                }
					freePlayMode = false;
					break;
				case SOLVE:
				case FREE_GUESS:
					suggestedSolution = currentContestant.getSolution();
					if (myPuzzle.evaluateSolution(suggestedSolution)) {
						solved = true;
					}
					else {
						for (int i = 1; i <=2; i++) Game.makeSound("buzzer");
						System.out.printf("\nSorry, %s, that is incorrect.\n", currentContestant.getName());
						// Add to list of guessed phrases
						myPuzzle.addGuessedPhrase(suggestedSolution);
						if (freePlayMode) {
							// Turn it off, it was already used.
							freePlayMode = false;
						}
						else {
							currentContestant = contestants.get(getNextContestant(contestants, currentContestant));					
						}
					}
					break;
				case WAVE_TO_VANNA:
					System.out.println(currentContestant.getName() + " waves to Vanna White.\n");
					currentContestant = contestants.get(getNextContestant(contestants, currentContestant));	
					break;
				default:
					break;
				}

				// End of turn actions
				if (!solved) {
					if (!myPuzzle.isMoreVowels()) System.out.println("There are no more vowels.");
					if (!myPuzzle.isMoreConsonants()) {
						Game.makeSound("only vowels");
						System.out.println("There are no more consonants.");
					}
					displayScoreboard(contestants);
					System.out.println("Category: " + myPuzzle.getPhrase().getCategory());
					myPuzzle.displayAnswer();
				}				
	        } 
	
			// End of round actions
			if (demoMode) {
				contestants.clear();
				contestants.add(human1);
				contestants.add(redQueen);
			}
			closeoutRound(myPuzzle, currentContestant, contestants);
        }
	}
	
	private static Puzzle getRoundTwoPuzzle() {
		System.out.println("Enter the puzzle phrase for round two");
		String phrase =  "";
		Category category;
		phrase = in.nextLine();
		category = getValidCategory();
		return new Puzzle(new Phrase(phrase, category));
	}
	
	public static Game.Category getValidCategory() {
	    boolean validCategory = false;
		while (!validCategory) {
			System.out.println("Enter the puzzle category for round two");
			String categoryString = in.nextLine().toUpperCase();
			for (Category cat : Game.Category.values()) {
		        if (cat.toString().equals(categoryString)) {
		            return cat;
		        }
		    }
		}
	    return null;
	}

	private static int getNextContestant(ArrayList<Contestant> contestants, Contestant currentContestant) {
		int nextContestant = contestants.indexOf(currentContestant) + 1;
		if (nextContestant == contestants.size()) nextContestant = 0;
		return nextContestant;
	}
	
	private static void displayScoreboard(ArrayList<Contestant> contestants) {
        for (int i=0; i<40; i++) System.out.print('-');
        System.out.println();
		for (Contestant myContestant : contestants) {
			System.out.printf("%12s: \t$%6d\t%6d\n", 
					myContestant.getName(), myContestant.getWinnings(), myContestant.getConfirmedWinnings());
		}
        for (int i=0; i<40; i++) System.out.print('-');
        System.out.println();
	}
	
	private static String getPersonName() {
			System.out.println("Welcome to the Wheel of Fortune.  What is your name?");
			return in.nextLine();
	}
	
	private static void setDemoMode() {
			System.out.println("Enter true for demo mode or false for play mode");
			demoMode =  Boolean.parseBoolean(in.nextLine());
	}
	
	private static void closeoutRound(Puzzle myPuzzle, Contestant winner, 
			                          ArrayList<Contestant> contestants) {
		myPuzzle.displayAnswer();
		System.out.printf("\n%s wins this round, with $%d!\n", 
					winner.getName(), winner.getWinnings());
		Game.makeSound("applause");
		for (Contestant myContestant : contestants) {
			if (myContestant instanceof Computer) {
				((Computer)myContestant).learnPuzzle(myPuzzle);
			}
			if (myContestant == winner) {
				myContestant.setConfirmedWinnings(myContestant.getConfirmedWinnings() + 
						                          myContestant.getWinnings());
			}
			// Set everyone's winnings back to zero
			myContestant.setWinnings(0);
		}
		// If there are AI Agents playing, they need a new winningsThreshold.
		updateWinningsThreshold(contestants);
		displayScoreboard(contestants);
	}

	private static void updateWinningsThreshold(ArrayList<Contestant> contestants) {
		int maxConfirmedWinnings = 0;
		for (Contestant c : contestants) {
			if (c.confirmedWinnings > maxConfirmedWinnings &&
				!(c instanceof AIagent3)) 
				maxConfirmedWinnings = c.confirmedWinnings;
		}
		for (Contestant c: contestants) {
			if (c instanceof AIagent3) {
				((AIagent3) c).setWinningsThreshold(maxConfirmedWinnings + 100);
			}
			if (c instanceof AIagent2) {
				((AIagent2) c).setWinningsThreshold(maxConfirmedWinnings + 100);
			}
			if (c instanceof AIagent) {
				((AIagent) c).setWinningsThreshold(maxConfirmedWinnings + 100);
			}
		}
	}
	

	
}
